﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace PhotoManager
{
    /// <summary>
    /// Represents a database of photo records.
    /// </summary>
    public class PhotoDatabase : IDisposable
    {
        private readonly Dictionary<string, PhotoItem> records =
            new Dictionary<string, PhotoItem>(); // record.Key => record
        private readonly Stream m_stream;
        private readonly StreamReader m_reader;
        private readonly StreamWriter m_writer;
        private bool m_isDisposed;

        public PhotoDatabase(string fileName)
        {
            if (fileName == null)
                throw new ArgumentNullException("fileName");

            m_stream = new FileStream(
                fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);

            // Create a stream reader and read existing records. Keep the
            // stream reader open to avoid closing the underlying stream.
            m_reader = new StreamReader(m_stream, Encoding.UTF8);
            for (string line; (line = m_reader.ReadLine()) != null; )
            {
                try
                {
                    PhotoItem r = PhotoItem.Parse(line);
                    records[ComputeKey(r)] = r;
                }
                catch (Exception) { }
            }

            // Create the stream writer to cache new records later.
            m_writer = new StreamWriter(m_stream, Encoding.UTF8);
            m_writer.AutoFlush = true;
        }

        private static string ComputeKey(string fileName, long fileSize, DateTime lastWriteTime)
        {
            return string.Format(
                    "{0}?{1}?{2:yyyyMMdd_HHmmss}",
                    fileName.ToLowerInvariant(), fileSize, lastWriteTime);
        }

        private static string ComputeKey(PhotoItem record)
        {
            return ComputeKey(record.FileName, record.FileSize, record.LastWriteTime);
        }

        private static string ComputeKey(FileInfo file)
        {
            return ComputeKey(file.FullName, file.Length, file.LastWriteTime);
        }

        /// <summary>
        /// Finds a record for the given file.
        /// </summary>
        /// <param name="file"></param>
        /// <returns>The photo record if one is found, or null otherwise.</returns>
        public PhotoItem FindRecord(FileInfo file)
        {
            if (file == null)
                throw new ArgumentNullException("file");

            PhotoItem r;
            records.TryGetValue(ComputeKey(file), out r);
            return r;
        }

        public void AddRecord(PhotoItem record)
        {
            if (record == null)
                throw new ArgumentNullException("record");
            if (m_isDisposed)
                throw new ObjectDisposedException("this");

            m_writer.WriteLine(record.ToString());
            records[ComputeKey(record)] = record;
        }

        public IEnumerable<PhotoItem> GetRecords()
        {
            return records.Values;
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected void Dispose(bool disposing)
        {
            if (disposing && !m_isDisposed)
            {
                m_writer.Dispose();
                m_reader.Dispose();
                m_stream.Dispose();
                m_isDisposed = true;
            }
        }
    }
}
